#include "fsxsimconnect.h"
using namespace FsxSimConnect;

//
// Static Callback from FSX
// pData    [in]  Pointer to a data buffer, to be treated initially as a SIMCONNECT_RECV structure. If you are going to make a copy of the data buffer (which is maintained by the SimConnect client library) make sure that the defined buffer is large enough (the size of the returned data structure is one member of the SIMCONNECT_RECV structure.
// cbData   [in]  The size of the data buffer, in bytes. -> DWORD cbData
// pContext [in]  Contains the pointer specified by the client in the SimConnect_CallDispatch function call.
//
void CALLBACK FSXSimConnect::simConnectDispatch(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
{
    // entry checks
    if (!pContext) return;
    FSXSimConnect* me = (FSXSimConnect*) pContext;
    if (!me->_runMessageLoop) return;
    const bool withStats = me->_settings.fsxCallbackStatistics;
    const qint64 statsTimeout = 45 * 1000;

    switch(pData->dwID)
    {
    case SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE:
    {
        SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE *evt = (SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE*)pData;
        SIMCONNECT_OBJECT_ID id = evt->dwData;
        bool isAircraft = (evt->eObjType == SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT || evt->eObjType == SIMCONNECT_SIMOBJECT_TYPE_HELICOPTER);
        switch(evt->uEventID)
        {
        case EVENT_ADDED_AIRCRAFT:
        {
            // ignore all other types
            if (isAircraft) {
                me->requestDataOnSimObject(id);
                QString s= QString("AI Object added: Type=%1, ObjectID=%2").arg(evt->eObjType).arg(id);
                emit me->signalLogMessage(s, QtDebugMsg);
                if (withStats) me->increaseStatisticsCount("EVENT_ADDED_AIRCRAFT");
            }
            break;
        } // case
        case EVENT_REMOVED_AIRCRAFT:
        {
            // ignore all other types
            if (isAircraft) {
                me->unrequestDataOnSimObject(id);
                QString s= QString("AI Object removed: Type=%1, ObjectID=%2").arg(evt->eObjType).arg(id);
                emit me->signalLogMessage(s, QtDebugMsg);
                me->_aircrafts.remove(id);
                if (withStats) me->increaseStatisticsCount("EVENT_REMOVED_AIRCRAFT");
            }
            break;
        } // case
        } // switch
        break;
    }
    case SIMCONNECT_RECV_ID_QUIT:
    {
        me->killSimConnect(); // Kill connection to FSX
        me->log("Quit received");
        emit me->signalQuit();
        break;
    }
    case SIMCONNECT_RECV_ID_OPEN:
    {
        int failures = 0;
        me->signalLogMessage("Open received!", QtDebugMsg);
        // request for own aircraft
        // failures = FSXSimConnect::requestDataOnSimObject();
        // me->log(QString("Request data on own aircraft, failures %1").arg(failures));

        // initial request list of aircrafts
        failures = me->requestAircraftData();
        me->log(QString("Request aircraft data, failures %1").arg(failures));

        break;
    }
    case SIMCONNECT_RECV_ID_EXCEPTION:
    {
        SIMCONNECT_RECV_EXCEPTION *except = (SIMCONNECT_RECV_EXCEPTION*)pData;
        QString ex = FSXSimConnectQtfier::simConnectExceptionToString(except->dwException);
        QString s = QString("Exception %1 SendId=%2 Index=%3 cbData=%4")
                .arg(ex)
                .arg(except->dwSendID)
                .arg(except->dwIndex)
                .arg(cbData);
        me->log(s);
        if (withStats) me->increaseStatisticsCount("SIMCONNECT_RECV_ID_EXCEPTION");
        break;
    }
    case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
    {
        SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA*) pData;
        SIMCONNECT_OBJECT_ID objectId = pObjData->dwObjectID;
        SIMCONNECT_DATA_REQUEST_ID requestId = pObjData->dwRequestID;
        // cast the individual request back to its base
        FSXDataDefinitionsHelper::Requests request = FSXDataDefinitionsHelper::getBaseRequestId(requestId);
        switch(request)
        {
        case FSXDataDefinitionsHelper::RequestAircraftPositionAndSpeedData:
        {
            FSXAircraftPositionAndSpeedData *pSD = (FSXAircraftPositionAndSpeedData*)&pObjData->dwData;
            me->_aircrafts.addOrUpdateById(objectId, *pSD, me->_settings.modelMaximumWaypointsNumber, me->_settings.modelWaypointsTimeFrameMs);
            if (withStats) me->increaseStatisticsCount("RequestAircraftPositionAndSpeedData");
            break;
        }
        case FSXDataDefinitionsHelper::RequestAircraftStringAndMiscData:
        {
            FSXAircraftStringAndMiscData *sD = (FSXAircraftStringAndMiscData*)&pObjData->dwData;
            me->_aircrafts.addOrUpdateById(objectId, *sD);
            if (withStats) me->increaseStatisticsCount("RequestAircraftStringData");
            break;
        }
        case FSXDataDefinitionsHelper::RequestAircraftInstrumentationData:
        {
            FSXAircraftInstrumentationData *iD = (FSXAircraftInstrumentationData*)&pObjData->dwData;
            me->_aircrafts.addOrUpdateById(objectId, *iD, me->_settings.modelMaximumWaypointsNumber, me->_settings.modelWaypointsTimeFrameMs);
            if (withStats) me->increaseStatisticsCount("RequestAircraftInstrumentationData");
            break;
        }
        case FSXDataDefinitionsHelper::RequestAircraftAvionicsData:
        {
            FSXAircraftAvionicsData *aD = (FSXAircraftAvionicsData*)&pObjData->dwData;
            // if (!aD->transponderAvailable) aD->transponderCode = -1;
            me->_aircrafts.addOrUpdateById(objectId, *aD);
            if (withStats) me->increaseStatisticsCount("RequestAircraftAvionicsData");
            break;
        }
        default:
            if (withStats) me->increaseStatisticsCount("SIMCONNECT_RECV_ID_SIMOBJECT_DATA default");
            break;
        }
        break;
    }
    case SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE:
    {
        SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE*)pData;
        SIMCONNECT_OBJECT_ID objectId = pObjData->dwObjectID;
        switch(pObjData->dwRequestID)
        {
        case FSXDataDefinitionsHelper::RequestAircraftMinimal:
        {
            // QString s = QString("Requesting data on id %1").arg(objectId);
            // me->log(s);
            me->requestDataOnSimObject(objectId);
            if (withStats) me->increaseStatisticsCount("FSXDataDefinitionsHelper::RequestAircraftMinimal");
            break;
        }
        default:
            break;
        }
        break;
    }
    default:
        break;
    }

    // writelog, check general counter
    me->increaseStatisticsCount("simConnectDispatch");
    int callbacks = me->_callbackStatistics.value("simConnectDispatch", 0);
    if (withStats && ((callbacks % 1500 == 0) || ((callbacks % 10 == 0) && me->_lastStatisticsLog + statsTimeout < QDateTime::currentMSecsSinceEpoch()))) {
        me->logStatistics();
    }
}
